﻿using System.Collections;
using System.Collections.Generic;
using System.Linq;
using FitNesseRunner;
using FitNesseRunner.Model;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace FitNesseRunnerTests
{
	[TestClass]
	public class PageParserTests
	{
		private static PageParser _parser;

		[TestMethod]
		public void EmptyPageReturnsNothing()
		{
			var elements = Parse();
			CollectionAssertEx.IsEmpty(elements);
		}

		[TestMethod]
		public void ParserRecognizesADirective()
		{
			TestDirective("directive1");
			TestDirective("directive2");
		}

		[TestMethod]
		public void ParserRecognizesMultipleDirectives()
		{
			var directives = Parse(
			                       "!directive1",
			                       "!directive2"
				).Cast<IDirective>();
			Assert.AreEqual("directive1", directives.ElementAt(0).Name);
			Assert.AreEqual("directive2", directives.ElementAt(1).Name);
		}

		[TestMethod]
		public void DirectivesAndRegularLinesInterleaved()
		{
			var directives = Parse("!directive1",
			                       "regular line",
			                       "!directive2").Cast<IDirective>();
			Assert.AreEqual("directive1", directives.ElementAt(0).Name);
			Assert.AreEqual("directive2", directives.ElementAt(1).Name);
		}

		[TestMethod]
		public void DirectivesCanHaveArguments()
		{
			var directive = Parse("!directive some arguments").Cast<IDirective>().Single();
			Assert.AreEqual("some arguments", directive.Arguments);
		}

		[TestMethod]
		public void ParserRecognizesAnEmptyTable()
		{
			var elements = Parse("||");
			var table = elements.Cast<ITable>().Single();
			CollectionAssertEx.AreEqual(string.Empty, table.Cells.Single().Single());
		}

		[TestMethod]
		public void ParserRecognizesAnEmptyTableEvenIfFollowedBySpaces()
		{
			var elements = Parse("||  ");
			var table = elements.Cast<ITable>().Single();
			CollectionAssertEx.AreEqual(string.Empty, table.Cells.Single().Single());
		}

		[TestMethod]
		public void LastVisibleCharacterOfTableLineMustBeAPipe()
		{
			var elements = Parse("||abc");
			CollectionAssertEx.IsEmpty(elements);
		}

		[TestMethod]
		public void SinglePipeIsNotRecognizedAsATable()
		{
			var elements = Parse("|");
			CollectionAssertEx.IsEmpty(elements);
		}

		[TestMethod]
		public void SingleCellTable()
		{
			var tables = Parse("|a|").Cast<ITable>();
			Assert.AreEqual("a", tables.Single().Cells.Single().Single());
		}

		[TestMethod]
		public void SingleLineTable()
		{
			var tables = Parse("|a|b| ").Cast<ITable>();
			var cells = tables.Single().Cells.Single();
			Assert.AreEqual(2, cells.Count());
			Assert.AreEqual("a", cells.ElementAt(0));
			Assert.AreEqual("b", cells.ElementAt(1));
		}

		[TestMethod]
		public void MultiLineTable()
		{
			var tables = Parse("|a|b|",
			                   "|c|d|").Cast<ITable>();
			Assert.AreEqual(1, tables.Count());
			var cells = tables.Single().Cells;
			Assert.AreEqual(2, cells.Count());
			Assert.AreEqual(2, cells.ElementAt(0).Count());
			Assert.AreEqual(2, cells.ElementAt(1).Count());
			Assert.AreEqual("a", cells.ElementAt(0).ElementAt(0));
			Assert.AreEqual("b", cells.ElementAt(0).ElementAt(1));
			Assert.AreEqual("c", cells.ElementAt(1).ElementAt(0));
			Assert.AreEqual("d", cells.ElementAt(1).ElementAt(1));
		}

		[TestMethod]
		public void TwoSeparatedTables()
		{
			var tables = Parse("|table1|",
			                   "not table",
			                   "|table2|").Cast<ITable>();
			Assert.AreEqual(2, tables.Count());
			Assert.AreEqual("table1", tables.ElementAt(0).Cells.Single().Single());
			Assert.AreEqual("table2", tables.ElementAt(1).Cells.Single().Single());
		}

		[TestMethod]
		public void TableWithEscapedLine()
		{
			var tables = Parse("!|a|").Cast<ITable>();
			Assert.AreEqual("a", tables.Single().Cells.Single().Single());
		}

		[TestMethod]
		public void EscapedLineStartsANewTable()
		{
			var tables = Parse("|a|",
			                   "!|b|").Cast<ITable>();
			Assert.AreEqual(2, tables.Count());
			Assert.AreEqual("a", tables.ElementAt(0).Cells.Single().Single());
			Assert.AreEqual("b", tables.ElementAt(1).Cells.Single().Single());
		}

		[TestMethod]
		public void HandleEscapedContent()
		{
			var tables = Parse("|!-escaped value-!|").Cast<ITable>();
			Assert.AreEqual("escaped value", tables.Single().Cells.At(0, 0));
		}

		private static void TestDirective(string directiveName)
		{
			var directiveLine = "!" + directiveName;
			var elements = Parse(directiveLine);
			Assert.AreEqual(1, elements.Count());
			Assert.AreEqual(directiveName, elements.Cast<IDirective>().Single().Name);
		}

		private static IEnumerable Parse(params string[] lines)
		{
			var pageObserver = new PageObserverDouble();
			_parser = new PageParser(pageObserver);
			_parser.Parse(lines);
			return pageObserver.Elements;
		}

		private class PageObserverDouble : IPageObserver
		{
			private readonly List<object> _elements = new List<object>();

			public void HandleDirective(IDirective directive)
			{
				_elements.Add(directive);
			}

			public void HandleTable(ITable table)
			{
				_elements.Add(table);
			}

			public IEnumerable Elements
			{
				get { return _elements; }
			}
		}
	}
}
